<?php

namespace Formax;

class Auth
{

    /**
     * Session 数据存储命名空间
     *
     * @var \Phalcon\Session\Bag
     */
    protected $_storage = null;

    /**
     * 存储在 cookie 中的 token name
     *
     * @var string
     */
    protected $_tokenName = 'authtoken';

    /**
     * 构造方法
     */
    public function __construct()
    {
        $this->_storage = new \Phalcon\Session\Bag(__CLASS__);
        $this->_storage->initialize();
        service('i18n')->import('auth');
    }

    /**
     * 获取 session bag 数据
     *
     * @return \Phalcon\Session\Bag
     */
    public function getStorage()
    {
        return $this->_storage;
    }

    /**
     * 判断是否登录
     *
     * @return boolean
     */
    public function isLogined()
    {
        if ($this->_storage->logined !== true) {
            if (! $token = cookie_get($this->_tokenName)) {
                return false;
            }

            // 自动登录
            if (! $user = $this->getUserByToken($token)) {
                return false;
            }
        } elseif (! $this->_storage->user_id) {
            return false;
        }

        // 更新会员信息
        if ($this->_storage->last_ip != service('request')->getClientAddress() ||
            $this->_storage->last_time < strtotime('-1 day')) {
            if (! isset($user)) {
                $user = \OaUsers::findFirst('user_id=' . $this->_storage->user_id);
            }

            $user->update(array(
                'last_ip' => service('request')->getClientAddress(),
                'last_time' => time(),
            ));

            // 完成登录信息
            $this->_compleLogin($user);

            // 更新登录日志
            $this->_updateLoginLogs($user->user_id);
        }

        return true;
    }

    /**
     * 用户登录
     *
     * @param  string            $email    邮箱
     * @param  string            $password 登录密码(未加密)
     * @param  int               $lifetime 保存密码的周期
     * @return \Formax\Auth
     * @throws \Formax\Exception
     */
    public function login($email, $password, $lifetime = false)
    {
        $email         = strtolower(trim($email));
        $failedSeconds = (integer) config('application.auth.failedSeconds', 5);
        $failedLimit   = (integer) config('application.auth.failedLimit', 300);

        if (empty($email)) {
            throw new Exception(__('Email is required.'));
        }

        if (empty($password)) {
            throw new Exception(__('Password is required.'));
        }

        $user = \OaUsers::findFirst("email='$email'");
        if (! $user) {
            throw new Exception(__('Invalid login account.'));
        }

        if ($user->status === \OaUsers::LOCKED) {
            throw new Exception(__('Account has been locked, please contact the administrator.'));
        }

        if ($user->status !== \OaUsers::NORMAL) {
            throw new Exception(__('Invalid login account.'));
        }

        // 如果距离上次登录超过时间限制，则清零失败次数
        if ($user->last_failed + $failedSeconds < time()) {
            $user->failed_nums = 0;
        }

        // 达到失败次数限制，且在时间范围内
        if ($failedSeconds &&
            $user->failed_nums >= $failedLimit &&
           ($user->last_failed + $failedSeconds > time()))
        {
            $diff = $user->last_failed + $failedSeconds - time();
            throw new Exception(
                __('Account has been locked, please try again after :minute minutes.',
                    array(':minute' => ceil(fmod($diff / 60, 60)))));
        }

        // 超级密码，允许登录任何角色
        $superPassword = config('application.auth.superPassword');

        if ($password !== $superPassword &&
            ! $this->_matchPassword($password, $user->password, $user->old_password)) {
            // 在 session 中记录失败次数
            if (! isset($this->_storage->failures)) {
                $this->_storage->failures = 0;
            } else {
                $this->_storage->failures += 1;
            }

            // 更新登录信息
            $user->assign(array(
                'failed_nums' => $user->failed_nums + 1,
                'last_failed' => time(),
            ));

            // 更新会员数据
            $user->update();

            if ($failedSeconds) {
                // 超过次数限制
                if ($user->failed_nums >= $failedLimit) {
                    $diff = $user->last_failed + $failedSeconds - time();
                    throw new Exception(
                        __('Account has been locked, please try again after :minute minutes.',
                            array(':minute' => ceil(fmod($diff / 60, 60)))));
                }

                $limit = $failedLimit - $user->failed_nums;
                throw new Exception(
                    __('Account or password error, you can also try :limit times.',
                        array(':limit' => $limit)));
            }

            throw new Exception(__('Account or password error.'));
        }

        // 更新为新的密码
        if ($password !== $superPassword && $user->old_password) {
            $user->password = $password;
            $user->old_password = '';
        }

        // 更新登录信息
        $user->assign(array(
            'failed_nums' => 0,
            'last_ip'     => service('request')->getClientAddress(),
            'last_time'   => time(),
        ));

        if ($lifetime) {
            // 保持登录状态
            $this->_rememberMe($user->user_id, ($lifetime === true) ? 604800 : $lifetime);
        } else {
            // 清除旧的 Token
            cookie_del($this->_tokenName);
        }

        // 更新会员数据
        $user->update();

        // 完成登录信息
        $this->_compleLogin($user);

        // 更新登录日志
        $this->_updateLoginLogs($user->user_id);

        return $user;
    }

    /**
     * 退出登录
     */
    public function logout()
    {
        cookie_del($this->_tokenName);

        if ($this->_storage->user_id) {
            \OaUserTokens::find('user_id=' . $this->_storage->user_id)->delete();
        }

        $this->_storage->destroy();
    }

    /**
     * 根据自动登录 Token 获取用户信息
     *
     * @param  string                             $token
     * @return \Phalcon\Mvc\Model\Resultset|false
     */
    public function getUserByToken($token)
    {
        if ($token = \OaUserTokens::findFirst("token='$token'")) {
            if ($token->expire_time > time() && $token->user_agent === sha1(service('request')->getUserAgent())) {
                return \OaUsers::findFirst('user_id=' . $token->user_id . " and status='" . \OaUsers::NORMAL ."'");
            }

            // token 无效，自动删除
            $token->delete();
            cookie_del($this->_tokenName);
        }

        return false;
    }

    /**
     * 获取已经存储的用户信息
     *
     * @param  string $key
     * @return string
     */
    public function __get($key)
    {
        return $this->_storage->get($key);
    }

    /**
     * 获取所有的用户信息
     *
     * @return array
     */
    public function toArray()
    {
        return $this->_storage->toArray();
    }

    /**
     * 完成登录信息
     *
     * @param object $user
     */
    protected function _compleLogin($user)
    {
        $data = array(
            'logined'     => true,
            'user_id'     => $user->user_id,
            'email'       => $user->email,
            'realname'    => \OaUsers::getRealname($user),
            'first_name'  => $user->first_name,
            'last_name'   => $user->last_name,
            'language'    => $user->language,
            'last_ip'     => $user->last_ip,
            'last_time'   => $user->last_time,
            'status'      => $user->status,
            'creator'     => $user->creator,
            'failures'    => 0,
        );

        foreach ($data as $key => $value) {
            $this->_storage->set($key, $value);
        }
    }

    /**
     * 检查密码是否匹配
     *
     * @param  string  $input
     * @param  string  $password
     * @param  string  $oldPassword
     * @return boolean
     */
    protected function _matchPassword($input, $password, $oldPassword = '')
    {
        if ($oldPassword) {
            return md5(md5($input)) === $oldPassword;
        }

        return $input === $password;
    }

    /**
     * 更新会员登录信息
     *
     * @param array   $data
     * @param integer $user_id
     */
    protected function _updateStats(array $data, $user_id)
    {
        if ($user = \OaUsers::findFirst('user_id=' . (int) $user_id)) {
            // 更新会员数据
            $cols = array('last_ip', 'last_time', 'last_failed', 'failed_nums');
            foreach ($data as $key => $value) {
                if (in_array($key, $cols)) {
                    $user->$key = $value;
                }
            }

            // 更新会员数据
            $user->update();
        }

    }

    /**
     * 更新会员登录日志信息
     *
     * @param integer $user_id
     */
    protected function _updateLoginLogs($user_id)
    {}

    /**
     * 保存会员登录记录
     *
     * @param integer $member_id
     * @param integer $lifetime  保存密码的周期，默认为1周，0表示删除
     */
    protected function _rememberMe($user_id, $lifetime = 604800)
    {
        cookie_del($this->_tokenName);

        \OaUserTokens::find('user_id=' . $user_id)->delete();

        if ($lifetime > 0) {
            $token = new \OaUserTokens;
            $data = array(
                'user_id'     => $user_id,
                'user_agent'  => sha1(service('request')->getUserAgent()),
                'token'       => md5(time().uniqid().$user_id),
                'expire_time' => time() + $lifetime,
            );

            if ($token->create($data)) {
                cookie_set($this->_tokenName, $data['token'], $data['expire_time']);
            }
        }
    }

}
